package edu.ksu.cis.indus.slicer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import soot.ArrayType;
import soot.Local;
import soot.RefType;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Type;
import soot.Value;
import soot.ValueBox;
import soot.jimple.ArrayRef;
import soot.jimple.FieldRef;
import soot.jimple.IdentityStmt;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.ParameterRef;
import soot.jimple.Stmt;
import soot.tagkit.Host;
import edu.ksu.cis.indus.common.ToStringBasedComparator;
import edu.ksu.cis.indus.common.datastructures.FIFOWorkBag;
import edu.ksu.cis.indus.common.datastructures.IWorkBag;
import edu.ksu.cis.indus.common.datastructures.Pair;
import edu.ksu.cis.indus.common.datastructures.PoolAwareWorkBag;
import edu.ksu.cis.indus.common.graph.IDirectedGraph;
import edu.ksu.cis.indus.common.graph.IDirectedGraph.INode;
import edu.ksu.cis.indus.common.graph.IMutableDirectedGraph;
import edu.ksu.cis.indus.common.graph.SimpleNodeGraph;
import edu.ksu.cis.indus.common.scoping.SpecificationBasedScopeDefinition;
import edu.ksu.cis.indus.common.soot.BasicBlockGraphMgr;
import edu.ksu.cis.indus.common.soot.Util;
import edu.ksu.cis.indus.interfaces.IActivePart;
import edu.ksu.cis.indus.interfaces.ICallGraphInfo;
import edu.ksu.cis.indus.interfaces.ICallGraphInfo.CallTriple;
import edu.ksu.cis.indus.interfaces.IEnvironment;
import edu.ksu.cis.indus.interfaces.INewExpr2InitMapper;
import edu.ksu.cis.indus.processing.Context;
import edu.ksu.cis.indus.staticanalyses.dependency.IDependencyAnalysis;
import edu.ksu.cis.indus.staticanalyses.processing.AnalysesController;

public final class SlicingEngine {
	public static final Object BACKWARD_SLICE = "BACKWARD_SLICE";

	public static final Object COMPLETE_SLICE = "COMPLETE_SLICE";

	public static final Object FORWARD_SLICE = "FORWARD_SLICE";
	public static final Collection SLICE_TYPES;
	static final Logger LOGGER = LoggerFactory.getLogger(SlicingEngine.class);
	SliceCollector collector;
	private final IActivePart.ActivePart activePart = new IActivePart.ActivePart();
	private AnalysesController controller;
	private BasicBlockGraphMgr bbgMgr;
	private final DependenceExtractor dependenceExtractor = new DependenceExtractor(
			this);

	private final IWorkBag workbag = new PoolAwareWorkBag(new FIFOWorkBag());

	private Collection controlflowBasedDAs = new ArrayList();
	private ICallGraphInfo cgi;
	private IDirectionSensitivePartOfSlicingEngine directionSensitiveInfo;
	private IEnvironment system;
	private INewExpr2InitMapper initMapper;
	private List criteria = new ArrayList();

	private Object sliceType = BACKWARD_SLICE;

	private Predicate nonStartMethodPredicate = new NonStartMethodPredicate();
	private SpecificationBasedScopeDefinition sliceScope;
	private Stack<CallTriple> callStackCache;
	private boolean useInterferenceDACache;
	private SimpleNodeGraph callStringGraph;

	static {
		Collection _c = new HashSet();
		_c.add(BACKWARD_SLICE);
		_c.add(FORWARD_SLICE);
		_c.add(COMPLETE_SLICE);
		SLICE_TYPES = Collections.unmodifiableCollection(_c);
	}

	public SlicingEngine() {
		this.collector = new SliceCollector(this);
		this.callStringGraph = new SimpleNodeGraph();
		this.callStringGraph.getNode(null);
	}

	public IActivePart getActivePart() {
		return this.activePart;
	}

	public void setAnalysesControllerAndDependenciesToUse(
			AnalysesController ctrl, Collection dependenciesToUse) {
		this.controller = ctrl;
		this.controlflowBasedDAs.clear();

		for (Iterator _i = dependenciesToUse.iterator(); _i.hasNext();) {
			Object _id = _i.next();

			if ((!(_id.equals(IDependencyAnalysis.CONTROL_DA)))
					&& (!(_id.equals(IDependencyAnalysis.SYNCHRONIZATION_DA)))
					&& (!(_id.equals(IDependencyAnalysis.DIVERGENCE_DA)))
					&& (!(_id.equals(IDependencyAnalysis.READY_DA))))
				continue;
			this.controlflowBasedDAs.addAll(this.controller.getAnalyses(_id));
		}
	}

	public void setBasicBlockGraphManager(BasicBlockGraphMgr basicBlockGraphMgr) {
		this.bbgMgr = basicBlockGraphMgr;
	}

	public void setCgi(ICallGraphInfo callgraph) {
		this.cgi = callgraph;
	}

	public SliceCollector getCollector() {
		return this.collector;
	}

	public void setDepID2ContextRetrieverMapping(Map map) {
		this.dependenceExtractor.setDepID2ContextRetrieverMapping(map);
	}

	public void setInitMapper(INewExpr2InitMapper mapper) {
		this.initMapper = mapper;
	}

	public void setSliceCriteria(Collection sliceCriteria) {
		if ((sliceCriteria == null) || (sliceCriteria.size() == 0)) {
			if (LOGGER.isWarnEnabled()) {
				LOGGER.warn("Slice criteria is unspecified.");
			}
			throw new IllegalStateException("Slice criteria is unspecified.");
		}
		if (this.controller == null) {
			if (LOGGER.isWarnEnabled()) {
				LOGGER.warn("Class Manager and/or Controller is unspecified.");
			}
			throw new IllegalStateException(
					"Class Manager and/or Controller is unspecified.");
		}

		SliceCriteriaFactory _criteriaFactory = SliceCriteriaFactory
				.getFactory();

		for (Iterator _i = sliceCriteria.iterator(); _i.hasNext();) {
			Object _o = _i.next();

			if (!(SliceCriteriaFactory.isSlicingCriterion(_o))) {
				LOGGER.error("The work piece is not a valid slice criterion."
						+ _o);
				throw new IllegalStateException(
						"The work piece is not a valid slice criterion." + _o);
			}

			this.criteria.add(_criteriaFactory.clone((ISliceCriterion) _o));
		}

		if (LOGGER.isDebugEnabled()) {
			Collections.sort(this.criteria, ToStringBasedComparator.SINGLETON);

			StringBuffer _sb = new StringBuffer();

			for (Iterator _i = this.criteria.iterator(); _i.hasNext();) {
				_sb.append("\n\t");
				_sb.append(_i.next());
			}
			LOGGER.debug("Criteria:\n" + _sb.toString());
			LOGGER.debug("END: Populating deadlock criteria.");
		}
	}

	public void setSliceScopeDefinition(SpecificationBasedScopeDefinition scope) {
		this.sliceScope = scope;
	}

	public void setSliceType(Object theSliceType) {
		if (!(SLICE_TYPES.contains(this.sliceType))) {
			throw new IllegalArgumentException(
					"The given slice type is not one of XXX_SLICE values defined in this class.");
		}

		this.sliceType = theSliceType;

		if (theSliceType.equals(BACKWARD_SLICE))
			this.directionSensitiveInfo = new BackwardSlicingPart(this);
		else if (theSliceType.equals(FORWARD_SLICE))
			this.directionSensitiveInfo = new ForwardSlicingPart(this);
		else if (theSliceType.equals(COMPLETE_SLICE)) {
			this.directionSensitiveInfo = new CompleteSlicingPart(this);
		}
		this.dependenceExtractor
				.setDependenceRetriever(this.directionSensitiveInfo);
	}

	public void setSystem(IEnvironment theSystem) {
		this.system = theSystem;
	}

	public IEnvironment getSystem() {
		return this.system;
	}

	public void setTagName(String tagName) {
		this.collector.setTagName(tagName);
	}

	public void enterMethod(ICallGraphInfo.CallTriple callsite) {
		if (callsite != null) {
			if (this.callStackCache == null) {
				this.callStackCache = new Stack();
			}
			this.callStackCache.push(callsite);
		}
	}

	public boolean ifInsideContext() {
		return ((this.callStackCache != null) && (!(this.callStackCache
				.isEmpty())));
	}

	public void initialize() {
		if (this.collector.getTagName() == null) {
			String _temp = "Please set the tag name before executing the engine.";
			LOGGER.error("Please set the tag name before executing the engine.");
			throw new IllegalStateException(
					"Please set the tag name before executing the engine.");
		}

		this.useInterferenceDACache = (this.controller
				.getAnalyses(IDependencyAnalysis.INTERFERENCE_DA) != null);
	}

	public void reset() {
		this.cgi = null;
		this.collector.reset();
		this.callStackCache = null;
		this.criteria.clear();
		this.activePart.activate();
		this.callStringGraph = new SimpleNodeGraph();
		this.callStringGraph.getNode(null);

		if (this.directionSensitiveInfo != null) {
			this.directionSensitiveInfo.reset();
		}

		this.workbag.clear();
	}

	public ICallGraphInfo.CallTriple returnFromMethod() {
		ICallGraphInfo.CallTriple _result = null;

		if ((this.callStackCache != null) && (ifInsideContext())) {
			_result = (ICallGraphInfo.CallTriple) this.callStackCache.pop();
		}

		return _result;
	}

	public void slice() {
		this.workbag.addAllWorkNoDuplicates(this.criteria);

		boolean _flag = true;

		while ((_flag) && (this.activePart.canProceed())) {
			while ((this.workbag.hasWork()) && (this.activePart.canProceed())) {
				Object _work = this.workbag.getWork();

				// TODO WXQ
				if (_work instanceof AbstractSliceCriterion) {
					System.out.println(_work);		
				}

				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug("BEGIN - Processing criterion - " + _work);
				}

				if (_work instanceof ExprLevelSliceCriterion) {
					ExprLevelSliceCriterion _sliceExpr = (ExprLevelSliceCriterion) _work;

					if ((this.sliceScope == null)
							|| (this.sliceScope.isInScope(
									_sliceExpr.getOccurringMethod(),
									this.system))) {
						this.callStackCache = _sliceExpr.getCallStack();
						transformAndGenerateNewCriteriaForExpr(_sliceExpr);
					}
				} else if (_work instanceof StmtLevelSliceCriterion) {
					StmtLevelSliceCriterion _sliceStmt = (StmtLevelSliceCriterion) _work;
					SootMethod _sm = _sliceStmt.getOccurringMethod();

					if ((this.sliceScope == null)
							|| (this.sliceScope.isInScope(_sm, this.system))) {

						Stmt _stmt = (Stmt) _sliceStmt.getCriterion();
						this.callStackCache = _sliceStmt.getCallStack();
						transformAndGenerateNewCriteriaForStmt(_stmt, _sm,
								_sliceStmt.isConsiderExecution());

					}
				} else if (_work instanceof MethodLevelSliceCriterion) {
					MethodLevelSliceCriterion _sliceMethod = (MethodLevelSliceCriterion) _work;
					SootMethod _sm = _sliceMethod.getOccurringMethod();

					// TODO WXQ
					/*
					 * System.out.println(_sm.getName());
					 * if(_sm.getName().equals("main")){ continue; }
					 */

					if ((this.sliceScope == null)
							|| (this.sliceScope.isInScope(_sm, this.system))) {
						this.callStackCache = _sliceMethod.getCallStack();
						transformAndGenerateNewCriteriaForMethod(_sm);
					}
				}

				if (LOGGER.isDebugEnabled()) {
					LOGGER.debug("END - Processing criterion - " + _work);
				}
			}
			_flag = this.directionSensitiveInfo.continueProcessing();
		}

		if (this.activePart.canProceed())
			this.collector.completeSlicing();
	}

	BasicBlockGraphMgr getBasicBlockGraphManager() {
		return this.bbgMgr;
	}

	ICallGraphInfo getCgi() {
		return this.cgi;
	}

	Stack getCopyOfCallStackCache() {
		final Stack<CallTriple> _result;

		if (callStackCache != null) {
			_result = (Stack<CallTriple>) callStackCache.clone();
		} else {
			_result = null;
		}
		return _result;
	}

	INewExpr2InitMapper getInitMapper() {
		return this.initMapper;
	}

	void generateCriteriaForTheCallToMethod(SootMethod method) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("BEGIN: Generating criteria for call-sites (callee-caller) "
					+ method);
		}

		if (isNotIncludedInSlice(method)) {
			includeMethodAndDeclaringClassInSlice(method);
		}

		if (!(haveCollectedAllInvocationSites(method))) {
			if (ifInsideContext()) {
				if (this.callStackCache.peek() != null) {
					ICallGraphInfo.CallTriple _top = (ICallGraphInfo.CallTriple) this.callStackCache
							.pop();
					SootMethod _caller = _top.getMethod();
					Stmt _stmt = _top.getStmt();
					this.directionSensitiveInfo
							.generateCriteriaForTheCallToMethod(method,
									_caller, _stmt);
					this.callStackCache.push(_top);
				}
			} else {
				markAsCollectedAllInvocationSites(method);

				for (Iterator _i = this.cgi.getCallers(method).iterator(); _i
						.hasNext();) {
					ICallGraphInfo.CallTriple _ctrp = (ICallGraphInfo.CallTriple) _i
							.next();
					SootMethod _caller = _ctrp.getMethod();
					Stmt _stmt = _ctrp.getStmt();
					this.directionSensitiveInfo
							.generateCriteriaForTheCallToMethod(method,
									_caller, _stmt);
				}
			}
		}

		if (LOGGER.isDebugEnabled())
			LOGGER.debug("END: Generating criteria for call-sites (callee-caller)");
	}

	boolean generateExprLevelSliceCriterion(ValueBox valueBox, Stmt stmt,
			SootMethod method, boolean considerExecution, Stack callStack) {
		Stack _stack = this.callStackCache;
		this.callStackCache = callStack;
		boolean _result = generateExprLevelSliceCriterion(valueBox, stmt,
				method, considerExecution);
		this.callStackCache = _stack;
		return _result;
	}

	boolean generateExprLevelSliceCriterion(ValueBox valueBox, Stmt stmt,
			SootMethod method, boolean considerExecution) {
		final boolean _result;

		if (isNotIncludedInSlice(valueBox)) {
			final Collection<ISliceCriterion> _sliceCriteria = SliceCriteriaFactory
					.getFactory().getCriteria(method, stmt, valueBox,
							considerExecution);
			setContext(_sliceCriteria);
			workbag.addAllWorkNoDuplicates(_sliceCriteria);

			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("Adding expr [" + considerExecution + "] "
						+ valueBox.getValue() + " at " + stmt + " in "
						+ method.getSignature() + " @ " + callStackCache
						+ " to workbag.");
			}
			_result = true;
		} else {
			if (valueBox.getValue() instanceof InvokeExpr) {
				generateCriteriaForInvokeExprIn(stmt, method);
			}

			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("Already collected expr " + valueBox.getValue()
						+ " occurring at " + stmt + " in "
						+ method.getSignature());
			}
			_result = false;
		}
		return _result;
	}

	boolean generateStmtLevelSliceCriterion(Stmt stmt, SootMethod method,
			boolean considerExecution) {
		boolean _result = isNotIncludedInSlice(stmt);

		if (_result) {
			Collection _sliceCriteria = SliceCriteriaFactory.getFactory()
					.getCriteria(method, stmt, considerExecution);
			setContext(_sliceCriteria);
			this.workbag.addAllWorkNoDuplicates(_sliceCriteria);

			if (LOGGER.isDebugEnabled())
				LOGGER.debug("Adding [" + considerExecution + "] " + stmt
						+ " in " + method.getSignature() + " @ "
						+ this.callStackCache + " to workbag.");
		} else {
			if ((this.sliceType.equals(COMPLETE_SLICE))
					|| ((considerExecution) && (this.sliceType
							.equals(BACKWARD_SLICE)))
					|| ((!(considerExecution)) && (this.sliceType
							.equals(FORWARD_SLICE)))) {
				Collection _temp = new HashSet(stmt.getUseAndDefBoxes());

				if (stmt.containsInvokeExpr()) {
					InvokeExpr _invokeExpr = stmt.getInvokeExpr();
					_temp.removeAll(_invokeExpr.getUseBoxes());

					if (_invokeExpr instanceof InstanceInvokeExpr) {
						_temp.add(((InstanceInvokeExpr) _invokeExpr)
								.getBaseBox());
					}
				}

				for (Iterator _i = _temp.iterator(); _i.hasNext();) {
					ValueBox _valueBox = (ValueBox) _i.next();
					generateExprLevelSliceCriterion(_valueBox, stmt, method,
							considerExecution);
				}
			}

			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("Already collected stmt " + stmt + " in "
						+ method.getSignature());
			}
		}
		return _result;
	}

	void includeMethodAndDeclaringClassInSlice(SootMethod method) {
		includeInSlice(method);

		SootClass _sc = method.getDeclaringClass();
		includeInSlice(_sc);

		Collection _types = new HashSet(method.getParameterTypes());
		_types.add(method.getReturnType());
		includeTypesInSlice(_types);

		List _exceptions = method.getExceptions();
		Iterator _i = _exceptions.iterator();
		int _iEnd = _exceptions.size();

		for (int _iIndex = 0; _iIndex < _iEnd; ++_iIndex) {
			SootClass _exception = (SootClass) _i.next();
			includeInSlice(_exception);
		}
	}

	private void setCallStackCache(Stack callStack) {
		this.callStackCache = callStack;
	}

	private void setContext(Collection theCriteria) {
		if (this.callStackCache != null) {
			Iterator _i = theCriteria.iterator();
			int _iEnd = theCriteria.size();

			for (int _iIndex = 0; _iIndex < _iEnd; ++_iIndex) {
				ISliceCriterion _criterion = (ISliceCriterion) _i.next();

				_criterion.setCallStack((Stack) this.callStackCache.clone());
			}
		}
	}

	private boolean isNotIncludedInSlice(Host host) {
		return (!(this.collector.hasBeenCollected(host)));
	}

	private void generateCriteriaBasedOnDependences(Stmt stmt,
			SootMethod method, Collection das) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("generateCriteriaBasedOnDependences(Stmt stmt=" + stmt
					+ ", SootMethod method=" + method
					+ ", Collection das) - BEGIN");
		}

		this.dependenceExtractor.setTrigger(stmt, method);
		CollectionUtils.forAllDo(das, this.dependenceExtractor);

		for (Iterator _i = this.dependenceExtractor.getDependences().iterator(); _i
				.hasNext();) {
			final Object _o = _i.next();
			final Collection _contexts = this.dependenceExtractor
					.getContextsFor(_o);
			final SootMethod _methodToBeIncluded;
			final Stmt _stmtToBeIncluded;

			/*
			 * if (_o instanceof Pair) { Pair _pair = (Pair)_o; Stmt
			 * _stmtToBeIncluded = (Stmt)_pair.getFirst(); _methodToBeIncluded =
			 * (SootMethod)_pair.getSecond(); } else { _stmtToBeIncluded =
			 * (Stmt)_o; _methodToBeIncluded = method; }
			 */

			if (_o instanceof Pair) {
				@SuppressWarnings("unchecked")
				final Pair<Stmt, SootMethod> _pair = (Pair) _o;
				_stmtToBeIncluded = _pair.getFirst();
				_methodToBeIncluded = _pair.getSecond();
			} else {
				_stmtToBeIncluded = (Stmt) _o;
				_methodToBeIncluded = method;
			}

			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug("generateCriteriaBasedOnDependences(Stmt, SootMethod, Collection) -  : _contexts="
						+ _contexts
						+ ", _stmtToBeIncluded="
						+ _stmtToBeIncluded
						+ ", _methodToBeIncluded="
						+ _methodToBeIncluded);
			}

			Stack _temp = getCopyOfCallStackCache();
			Iterator _i1 = _contexts.iterator();
			int _iEnd = _contexts.size();

			for (int _iIndex = 0; _iIndex < _iEnd; ++_iIndex) {
				Stack _context = (Stack) (Stack) _i1.next();
				setCallStackCache(_context);

				if (_stmtToBeIncluded != null) {
					boolean _b = generateStmtLevelSliceCriterion(
							_stmtToBeIncluded, _methodToBeIncluded, true);

					if (!(_b))
						generateMethodLevelSliceCriteria(_methodToBeIncluded);
				} else {
					generateMethodLevelSliceCriteria(_methodToBeIncluded);
				}
			}
			setCallStackCache(_temp);
		}

		if (LOGGER.isDebugEnabled())
			LOGGER.debug("generateCriteriaBasedOnDependences(Stmt, SootMethod, Collection) - END");
	}

	private void generateCriteriaForInvokeExprIn(Stmt stmt, SootMethod method) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("BEGIN: Generating criteria for invocation expressions (caller-callee)");
		}

		InvokeExpr _expr = stmt.getInvokeExpr();
		SootMethod _sm = _expr.getMethod();
		Collection _callees = new HashSet();

		if (_sm.isStatic()) {
			_callees.add(_sm);
		} else {
			Context _context = new Context();
			_context.setRootMethod(method);
			_context.setStmt(stmt);
			_callees.addAll(this.cgi.getCallees(_expr, _context));
		}

		CollectionUtils.filter(_callees, this.nonStartMethodPredicate);
		this.directionSensitiveInfo.generateCriteriaToIncludeCallees(stmt,
				method, _callees);

		includeMethodAndDeclaringClassInSlice(_sm);

		if (LOGGER.isDebugEnabled())
			LOGGER.debug("END: Generating criteria for invocation expressions (caller-callee)");
	}

	private void generateCriteriaForLocals(Collection locals, Stmt stmt,
			SootMethod method) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("generateCriteriaForLocals(Collection locals = "
					+ locals + ", Stmt stmt = " + stmt
					+ ", SootMethod method = " + method + ", stack ="
					+ this.callStackCache + ") - BEGIN");
		}

		Collection _analyses = this.controller
				.getAnalyses(IDependencyAnalysis.IDENTIFIER_BASED_DATA_DA);

		if (!(_analyses.isEmpty())) {
			Iterator _k = locals.iterator();
			int _kEnd = locals.size();

			for (int _kIndex = 0; _kIndex < _kEnd; ++_kIndex) {
				ValueBox _vBox = (ValueBox) _k.next();
				Local _local = (Local) _vBox.getValue();
				Pair _pair = new Pair(stmt, _local);
				this.dependenceExtractor.setTrigger(_pair, method);
				CollectionUtils.forAllDo(_analyses, this.dependenceExtractor);

				Collection _dependences = this.dependenceExtractor
						.getDependences();
				Iterator _l = _dependences.iterator();
				int _lEnd = _dependences.size();

				for (int _lIndex = 0; _lIndex < _lEnd; ++_lIndex) {
					Stmt _depStmt = (Stmt) _l.next();

					this.directionSensitiveInfo.processLocalAt(_local,
							_depStmt, method);
				}

				this.directionSensitiveInfo
						.processLocalAt(_local, stmt, method);
			}
		}

		if (LOGGER.isDebugEnabled())
			LOGGER.debug("generateCriteriaForLocals() , stack ="
					+ this.callStackCache + "- END");
	}

	private void generateMethodLevelSliceCriteria(SootMethod method) {
		boolean _generateCriteria = shouldMethodLevelCriteriaBeGenerated(method);

		if (_generateCriteria) {
			Collection _sliceCriteria = SliceCriteriaFactory.getFactory()
					.getCriteria(method);
			setContext(_sliceCriteria);

			Collection _c = this.workbag.addAllWorkNoDuplicates(_sliceCriteria);

			if ((LOGGER.isDebugEnabled()) && (!(_c.isEmpty())))
				LOGGER.debug("Adding " + method.getSignature() + " @ "
						+ this.callStackCache + " to workbag.");
		}
	}

	private void includeInSlice(Host host) {
		this.collector.includeInSlice(host);
	}

	private void includeTypesInSlice(Collection types) {
		for (Iterator _i = types.iterator(); _i.hasNext();) {
			Type _type = (Type) _i.next();

			if (_type instanceof RefType)
				includeInSlice(((RefType) _type).getSootClass());
			else if ((_type instanceof ArrayType)
					&& (((ArrayType) _type).baseType instanceof RefType))
				includeInSlice(((RefType) ((ArrayType) _type).baseType)
						.getSootClass());
		}
	}

	private boolean recordCallStackForMethod(SootMethod method) {
		boolean _result = true;

		final INode _methodNode = callStringGraph.getNode(method);
		if (callStringGraph.queryNode(method) == null) {
			if (!callStackCache.isEmpty()) {
				Object _s = callStackCache.peek();
				if (_s != null) {
					callStringGraph.addEdgeFromTo(_methodNode,
							callStringGraph.getNode(_s));

					for (int _i = callStackCache.size() - 2; _i >= 0; _i--) {
						final Object _t = callStackCache.get(_i);
						if (_t != null) {
							callStringGraph.addEdgeFromTo(
									callStringGraph.queryNode(_s),
									callStringGraph.getNode(_t));
							_s = _t;
						} else {
							break;
						}
					}
				}
			}
		} else if (!callStackCache.isEmpty()) {
			_result = recordCallStackForVisitedMethod(_methodNode);
		}
		return _result;
	}

	private boolean recordCallStackForVisitedMethod(
			IDirectedGraph.INode methodNode) {
		int _limit = this.callStackCache.size() - 1;
		boolean _considerCallStack = this.callStackCache.peek() != null;
		boolean _createdNewNodes = false;

		for (int _i = _limit; (_i >= 0) && (_considerCallStack); --_i) {
			Object _o = this.callStackCache.get(_i);

			IDirectedGraph.INode _node = this.callStringGraph.queryNode(_o);

			if (_node == null) {
				_node = this.callStringGraph.getNode(_o);
				_createdNewNodes = true;
			} else if (_o != null) {
				if (_node.getSuccsOf().isEmpty()) {
					_considerCallStack = false;
				} else if (_i == 0) {
					Collection _reachablesFrom = this.callStringGraph
							.getReachablesFrom(_node, true);
					_reachablesFrom.add(_node);

					Iterator _j = _reachablesFrom.iterator();
					int _jEnd = _reachablesFrom.size();

					for (int _jIndex = 0; _jIndex < _jEnd; ++_jIndex) {
						IDirectedGraph.INode _src = (IDirectedGraph.INode) _j
								.next();
						Collection _succsOfSrc = new ArrayList(
								_src.getSuccsOf());

						Iterator _k = _succsOfSrc.iterator();
						int _kEnd = _succsOfSrc.size();

						for (int _kIndex = 0; _kIndex < _kEnd; ++_kIndex) {
							IDirectedGraph.INode _dest = (IDirectedGraph.INode) _k
									.next();
							this.callStringGraph.removeEdgeFromTo(_src, _dest);
						}
					}
					_considerCallStack = false;
				}
			} else {
				_considerCallStack = false;
			}

			if ((_limit - _i > 0)
					&& (((_considerCallStack) || (_createdNewNodes)))) {
				IDirectedGraph.INode _prev = this.callStringGraph
						.queryNode(this.callStackCache.get(_i + 1));

				if (_prev != null) {
					this.callStringGraph.addEdgeFromTo(_prev, _node);
				}
			}
		}

		this.callStringGraph.addEdgeFromTo(methodNode,
				this.callStringGraph.getNode(this.callStackCache.peek()));
		return ((_considerCallStack) || (_createdNewNodes));
	}

	private boolean haveCollectedAllInvocationSites(SootMethod method) {
		return ((this.callStringGraph.queryNode(method) != null) && (this.callStringGraph
				.queryNode(method).getSuccsOf().isEmpty()));
	}

	private void markAsCollectedAllInvocationSites(SootMethod method) {
		IMutableDirectedGraph.IMutableNode _n = (IMutableDirectedGraph.IMutableNode) this.callStringGraph
				.getNode(method);
		for (Iterator _i = new ArrayList(_n.getSuccsOf()).iterator(); _i
				.hasNext();)
			_n.removeSuccessor((IDirectedGraph.INode) _i.next());
	}

	private boolean shouldMethodLevelCriteriaBeGenerated(SootMethod method) {
		boolean _result = isNotIncludedInSlice(method);

		if (!(_result)) {
			if (this.callStackCache == null)
				_result = !(haveCollectedAllInvocationSites(method));
			else if (!(haveCollectedAllInvocationSites(method))) {
				_result = recordCallStackForMethod(method);
			}
		}

		return _result;
	}

	private void transformAndGenerateNewCriteriaForExpr(
			ExprLevelSliceCriterion expr) {
		Stmt _stmt = expr.getOccurringStmt();
		SootMethod _method = expr.getOccurringMethod();
		ValueBox _vBox = (ValueBox) expr.getCriterion();
		boolean _considerExecution = expr.isConsiderExecution();
		Value _value = _vBox.getValue();

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("BEGIN: Transforming expr criteria: " + _value + "["
					+ _considerExecution + "] at " + _stmt + " in " + _method
					+ "   " + this.callStackCache);
		}

		if (this.sliceType.equals(FORWARD_SLICE))
			transformAndGenerateNewCriteriaForStmt(_stmt, _method, true);
		else {
			transformAndGenerateNewCriteriaForStmt(_stmt, _method, false);
		}

		if ((this.sliceType.equals(COMPLETE_SLICE))
				|| ((_considerExecution) && (this.sliceType
						.equals(BACKWARD_SLICE)))
				|| ((!(_considerExecution)) && (this.sliceType
						.equals(FORWARD_SLICE)))) {
			Collection _valueBoxes = this.directionSensitiveInfo
					.retrieveValueBoxesToTransformExpr(_vBox, _stmt);

			transformAndGenerateToConsiderExecution(_stmt, _method, _valueBoxes);

			if (_value instanceof InvokeExpr) {
				generateCriteriaForInvokeExprIn(_stmt, _method);
			}
		}

		if (LOGGER.isDebugEnabled())
			LOGGER.debug("END: Transforming expr criteria: " + _value + " at "
					+ _stmt + " in " + _method);
	}

	private void transformAndGenerateNewCriteriaForMethod(SootMethod method) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("transformAndGenerateNewCriteriaForMethod(SootMethod method = "
					+ method + ") - BEGIN");
		}

		generateCriteriaForTheCallToMethod(method);
		generateCriteriaBasedOnDependences(null, method,
				this.controlflowBasedDAs);

		if (this.callStackCache == null)
			markAsCollectedAllInvocationSites(method);
		else {
			recordCallStackForMethod(method);
		}

		if (LOGGER.isDebugEnabled())
			LOGGER.debug("transformAndGenerateNewCriteriaForMethod() - END");
	}

	private void transformAndGenerateNewCriteriaForStmt(Stmt stmt,
			SootMethod method, boolean considerExecution) {
		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("BEGIN: Transforming stmt criteria: " + stmt + "["
					+ considerExecution + "] in " + method + "   "
					+ this.callStackCache);
		}

		if ((this.sliceType.equals(COMPLETE_SLICE))
				|| ((considerExecution) && (this.sliceType
						.equals(BACKWARD_SLICE)))
				|| ((!(considerExecution)) && (this.sliceType
						.equals(FORWARD_SLICE)))) {
			this.directionSensitiveInfo.processNewExpr(stmt, method);

			Collection _valueBoxes = this.directionSensitiveInfo
					.retrieveValueBoxesToTransformStmt(stmt);
			transformAndGenerateToConsiderExecution(stmt, method, _valueBoxes);

			if (stmt.containsInvokeExpr()) {
				generateCriteriaForInvokeExprIn(stmt, method);
			}

		}

		if (isNotIncludedInSlice(stmt)) {
			generateCriteriaBasedOnDependences(stmt, method,
					this.controlflowBasedDAs);
		}

		generateCriteriaForTheCallToMethod(method);

		includeInSlice(stmt);

		if (LOGGER.isDebugEnabled())
			LOGGER.debug("END: Transforming stmt criteria: " + stmt + "["
					+ considerExecution + "] in " + method);
	}

	private void transformAndGenerateToConsiderExecution(Stmt stmt,
			SootMethod method, Collection vBoxes) {
		if (LOGGER.isDebugEnabled()) {
			StringBuffer _sb = new StringBuffer();
			_sb.append("BEGIN: Transforming value boxes [");

			for (Iterator _i = vBoxes.iterator(); _i.hasNext();) {
				ValueBox _vBox = (ValueBox) _i.next();
				_sb.append(_vBox.getValue());
				_sb.append("[" + _vBox + "]");
				_sb.append(", ");
			}
			_sb.append("]");
			LOGGER.debug(_sb.toString());
		}

		Collection _types = new HashSet();
		Collection _das = new ArrayList();
		Collection _locals = new HashSet();

		for (Iterator _i = vBoxes.iterator(); _i.hasNext();) {
			ValueBox _vBox = (ValueBox) _i.next();

			if (isNotIncludedInSlice(_vBox)) {
				includeInSlice(_vBox);

				Value _value = _vBox.getValue();

				if (_value instanceof ParameterRef) {
					this.directionSensitiveInfo.processParameterRef(
							(IdentityStmt) stmt, method);
				} else if ((_value instanceof FieldRef)
						|| (_value instanceof ArrayRef)) {
					_das.addAll(this.controller
							.getAnalyses(IDependencyAnalysis.REFERENCE_BASED_DATA_DA));

					if (this.useInterferenceDACache) {
						_das.addAll(this.controller
								.getAnalyses(IDependencyAnalysis.INTERFERENCE_DA));
					}

					if (_value instanceof FieldRef) {
						SootField _field = ((FieldRef) _vBox.getValue())
								.getField();
						includeInSlice(_field);
						includeInSlice(_field.getDeclaringClass());
					}
				} else if (_value instanceof Local) {
					_locals.add(_vBox);
				}
				_types.add(_value.getType());
			}
		}
		includeTypesInSlice(_types);

		if (!(_das.isEmpty())) {
			generateCriteriaBasedOnDependences(stmt, method, _das);
		}

		generateCriteriaForLocals(_locals, stmt, method);

		if (LOGGER.isDebugEnabled())
			LOGGER.debug("END: Transforming value boxes");
	}

	private static class NonStartMethodPredicate implements Predicate {
		public boolean evaluate(Object object) {
			return (!(Util.isStartMethod((SootMethod) object)));
		}
	}
}

/*
 * Location: D:\tool\banderaCLI-1.0a4\lib\indus\slicer.jar Qualified Name:
 * edu.ksu.cis.indus.slicer.SlicingEngine Java Class Version: 5 (49.0) JD-Core
 * Version: 0.5.3
 */